package differences;

import hips.Image;
import hips.Partition;
import hips.PixelValue;
import hips.Region;
import hips.gui.ProgressBarFrame;
import hips.gui.parameters.ParameterFrame;
import hips.gui.parameters.ParameterListener;
import hips.images.integer.rgb.ImageRGB;
import hips.images.integer.rgb.PixelValueRGB;
import hips.region.NewRegionEvent;
import hips.region.NewRegionListener;
import ij.IJ;
import ij.ImagePlus;
import ij.process.ColorProcessor;
import java.io.File;
import javax.swing.UIManager;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class Main implements ParameterListener, NewRegionListener {

	int imagesOpened;
	Partition youngest;
	Partition oldest;
	ProgressBarFrame progress;
	long completed;
	int[] pixels;
	int[] references;
	int[] lbl;
	int label;
	Region common;
	ImageRGB output;
	protected MainFrame frame;

	public Main() {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception ex) {
		}
		frame = new MainFrame();
		frame.setCore(this);
		imagesOpened = 0;
		common = null;
		frame.allowGetYoungestImage();
		frame.allowGetOldestImage();
		frame.dontAllowCustomizeParameters();
		frame.dontAllowGetDifferences();
	}

	public void customizeParameters() {
		Image img = youngest.getImage();
		if (img == null) {
			img = oldest.getImage();
		}
		ParameterFrame p = new ParameterFrame(img);
		p.addParameterListener(this);
		frame.setEnabled(false);
		p.setVisible(true);
	}

	public void ParametersOk(int type, PixelValue alpha, PixelValue omega, float cindex, int randomPixels, int iterations) {
		youngest.setAlpha(alpha);
		youngest.setOmega(omega);
		youngest.setCindex(cindex);
		youngest.setRandomPixels(randomPixels);
		oldest.setAlpha(alpha);
		oldest.setOmega(omega);
		oldest.setCindex(cindex);
		oldest.setRandomPixels(randomPixels);
		frame.setEnabled(true);
	}

	public void ParametersCancelled() {
		frame.setEnabled(true);
	}

	public void getYoungestImage(File file) {
		Image img;
		if (imagesOpened == 0) {
			img = checkFirstImage(new ImagePlus(file.getAbsolutePath()));
			if (img != null) {
				imagesOpened++;
				frame.dontAllowGetYoungestImage();
			}
		} else {
			img = checkSecondImage(new ImagePlus(file.getAbsolutePath()), oldest.getImage());
			if (img != null) {
				imagesOpened++;
				frame.dontAllowGetOldestImage();
				frame.allowGetDifferences();
				frame.allowCustomizeParameters();
			}
		}
		if (img != null) {
			img.getImagePlus().show();
			youngest = img.newPartition(Partition.IMPROVED_WITH_CINDEX);

		}
	}

	public void getOldestImage(File file) {
		Image img;
		if (imagesOpened == 0) {
			img = checkFirstImage(new ImagePlus(file.getAbsolutePath()));
			if (img != null) {
				imagesOpened++;
				frame.dontAllowGetOldestImage();
			}
		} else {
			img = checkSecondImage(new ImagePlus(file.getAbsolutePath()), youngest.getImage());
			if (img != null) {
				imagesOpened++;
				frame.dontAllowGetOldestImage();
				frame.allowGetDifferences();
				frame.allowCustomizeParameters();
			}
		}
		if (img != null) {
			img.getImagePlus().show();
			oldest = img.newPartition(Partition.IMPROVED_WITH_CINDEX);
		}
	}

	public void getDifferences() {
		frame.dontAllowCustomizeParameters();
		youngest.addNewRegionEventListener(this);
		oldest.addNewRegionEventListener(this);
		completed = 0;
		progress = new ProgressBarFrame();
		progress.setText("creating partitions for the images");
		Thread t = new Thread(new Runnable() {

			public void run() {
				youngest.makeRegions();
				oldest.makeRegions();
				paintDifferences(oldest, calculateDifferences(oldest, youngest));
			}
		});
		t.start();
	}

	public void newRegionCreated(NewRegionEvent e) {
		completed += e.getRegion().getSize();
		progress.setPercentage(((float) completed) / ((float) (youngest.getImage().getSize() * 2)));
		if (completed == (youngest.getImage().getSize() * 2)) {
			progress.dispose();
		}
	}

	private float[] calculateDifferences(Partition a, Partition b) {

		pixels = new int[youngest.getImage().getSize()];
		references = new int[youngest.getImage().getSize()];
		lbl = new int[youngest.getImage().getSize()];
		float[] diff = new float[youngest.getImage().getSize()];

		// diff y references podrian ser de un tamaño menor

		int base = 0;
		label = 0;

		for (int i = 0; i < a.getRegionSize(); i++) {
			Region r = a.getRegionByLabel(i);
			for (int j = 0; j < r.getSize(); j++) {
				lbl[r.getPixelPosition(j)] = Integer.MAX_VALUE;
			}

			for (int j = 0; j < r.getSize(); j++) {
				if (lbl[r.getPixelPosition(j)] == Integer.MAX_VALUE) {
					Region r2 = b.getRegionByPixel(r.getPixelPosition(j));

					int size = 0;
					for (int k = 0; k < r2.getSize(); k++) {
						if (lbl[r2.getPixelPosition(k)] == Integer.MAX_VALUE) {
							lbl[r2.getPixelPosition(k)] = label;
							pixels[base + size] = r2.getPixelPosition(k);
							size++;
						}
					}
					references[label] = base;
					base += size;

					float mult = size * (r2.getSize() - size) * (r.getSize() - size);

					float div = r2.getSize() + r.getSize() - size;
					float div3 = div / 3;

					float result;

					if (div % 3 == 0) {
						result = mult / (div3 * div3 * div3);
					} else if (div % 3 == 1) {
						result = mult / (div3 * div3 * (div3 + 1));
					} else {
						result = mult / (div3 * (div3 + 1) * (div3 + 1));
					}

					float log1 = (float) (Math.log10(r.getSize()) + 1);
					float log2 = (float) (Math.log10(r2.getSize()) + 1);

					float maxLog = (log1 + log2) / 2;

					diff[label] = result * (log1 * log2) / (maxLog * maxLog);
					label++;

				}
			}
		}
		references[label] = base;
		return diff;
	}

	// 0.0 = verde, 1.0 = rojo
	private void paintDifferences(Partition p, float[] diff) {
		output = new ImageRGB(new ImagePlus("output", new ColorProcessor(oldest.getImage().getWidth(), oldest.getImage().getHeight())));
		output.getImagePlus().show();
		for (int i = 0; i < label; i++) {
			int size = references[i + 1] - references[i];
			Region r = new Region(pixels, references[i], size, 0);
			int R, G;
			if (diff[i] < 0.5f) {
				G = 255;
				R = (int) (511 * diff[i]);
			} else {
				R = 255;
				G = (int) (512 * (1f - diff[i]));
			}
			PixelValueRGB pv = output.newPixelValue();
			pv.setValue(R, 0, 0);
			pv.setValue(G, 0, 1);
			pv.setValue(0, 0, 2);
			output.paintRegion(r, pv);
		}
		output.getImagePlus().updateAndDraw();
	}

	public static Image checkFirstImage(ImagePlus imp) {
		if (imp == null) {
			IJ.error("ERROR", "The are no images open.");
			return null;
		}
		if (!imp.isProcessor()) {
			IJ.error("ERROR", "The ImagePlus opened is not valid.");
			return null;
		}
		Image img = Image.getImage(imp);
		if (img == null) {
			IJ.error("ERROR", "The type image is not valid for this plugin.");
		}
		return img;
	}

	public static Image checkSecondImage(ImagePlus imp, Image first) {
		Image second = checkFirstImage(imp);
		if (second == null) {
			return null;
		}
		if (second.getSlices() != first.getSlices()) {
			IJ.error("ERROR", "The ImagePlus opened has not the same stack size than the first");
			return null;
		}
		if (second.getClass() != first.getClass()) {
			IJ.error("ERROR", "The ImagePlus opened has not the same type than the first");
			return null;
		}
		if (second.getHeight() != first.getHeight()) {
			IJ.error("ERROR", "The ImagePlus opened has not the same height than the first");
			return null;
		}
		if (second.getWidth() != first.getWidth()) {
			IJ.error("ERROR", "The ImagePlus opened has not the same width than the first");
			return null;
		}
		return second;
	}
}
